﻿using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace ELinqParser
{
	public static class LinqKnownMembers
	{
		// ReSharper disable ReturnValueOfPureMethodIsNotUsed
		public static MethodInfo GetMethodInfo(Expression<Action> expression)
		{
			return GetMethodInfo((LambdaExpression)expression);
		}

		public static MethodInfo GetMethodInfo<T>(Expression<Action<T>> expression)
		{
			return GetMethodInfo((LambdaExpression)expression);
		}

		public static MethodInfo GetMethodInfo<T, TResult>(Expression<Func<T, TResult>> expression)
		{
			return GetMethodInfo((LambdaExpression)expression);
		}

		public static MethodInfo GetMethodInfo(LambdaExpression expression)
		{
			var outermostExpression = expression.Body as MethodCallExpression;

			if (outermostExpression == null)
			{
				throw new ArgumentException("Invalid Expression. Expression should consist of a Method call only.");
			}

			var methodInfo = outermostExpression.Method;
			if (methodInfo.IsGenericMethod)
 				methodInfo = methodInfo.GetGenericMethodDefinition();
			return methodInfo;
		}

		public static MemberInfo GetMemberInfo<TProperty>(Expression<Func<TProperty>> expression)
		{
			if (expression == null)
				throw new ArgumentNullException("expression");

			var lambda = expression as LambdaExpression;
			var unary = lambda.Body as UnaryExpression;

			MemberExpression member = unary != null 
				? unary.Operand as MemberExpression 
				: lambda.Body as MemberExpression;

			if (member == null)
				throw new ArgumentException("Please provide a lambda expression like 'n => n.PropertyName'");

			return member.Member;
		}

		static MethodInfo _queryableSelect1;

		public static MethodInfo QueryableSelect1
		{
			get { return _queryableSelect1 ?? (_queryableSelect1 = GetMethodInfo(() => ((IQueryable<int>) null).Select(p => 0))); }
		}

		static MethodInfo _queryableSelect2;
		public static MethodInfo QueryableSelect2
		{
			get { return _queryableSelect2 ?? (_queryableSelect2 = GetMethodInfo(() => ((IQueryable<int>) null).Select((p,i) => 0))); }
		}

		static MethodInfo _queryableCountWithFunc;
		public static MethodInfo QueryableCountWithFunc
		{
			get { return _queryableCountWithFunc ?? (_queryableCountWithFunc = GetMethodInfo(() => ((IQueryable<int>) null).Count(p => true))); }
		}

		static MethodInfo _queryableCount;
		public static MethodInfo QueryableCount
		{
			get { return _queryableCount ?? (_queryableCount = GetMethodInfo(() => ((IQueryable<int>) null).Count())); }
		}		
		
		static MethodInfo _queryableGroupBy;
		public static MethodInfo QueryableGroupBy
		{
			get { return _queryableGroupBy ?? (_queryableGroupBy = GetMethodInfo(() => ((IQueryable<int>)null).GroupBy(m => 0))); }
		}

		static MemberInfo _groupingKey;
		public static MemberInfo GroupingKey
		{
			get { return _groupingKey ?? (_groupingKey = GetMemberInfo(() => ((IGrouping<int, int>) null).Key)); }
		}

		public static bool Same(this MemberInfo mi1, MemberInfo mi2)
		{
			if (mi1.Name != mi2.Name)
				return false;
			if (mi1.ReflectedType.IsGenericType)
				return mi1.ReflectedType.GetGenericTypeDefinition() == mi2.ReflectedType.GetGenericTypeDefinition();
			return mi1.ReflectedType == mi2.ReflectedType;
		}
		// ReSharper restore ReturnValueOfPureMethodIsNotUsed
	}
}